package com.alfred.java.customannotationaop.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.lang.reflect.Modifier;

/**
 * @Author: alfred
 * @Date: 2021/1/28
 * @description:
 */
@Slf4j
@Aspect
@Component
public class MyLogAspect {

    @Pointcut("@annotation(com.alfred.java.customannotationaop.annotation.MyLog)")
    public void log(){
    }

    @Before("log()")
    public void doBefore(JoinPoint joinPoint){
        log.info("—————————————————doBefore—————————————————");
        log.info("目标方法名为:" + joinPoint.getSignature().getName());
        log.info("目标方法所属类的简单类名:" + joinPoint.getSignature().getDeclaringType().getSimpleName());
        log.info("目标方法所属类的类名:" + joinPoint.getSignature().getDeclaringTypeName());
        log.info("目标方法声明类型:" + Modifier.toString(joinPoint.getSignature().getModifiers()));
        //获取传入目标方法的参数
        Object[] args = joinPoint.getArgs();
        for (int i = 0; i < args.length; i++) {
            log.info("第" + (i+1) + "个参数为:" + args[i]);
        }
        log.info("被代理的对象:" + joinPoint.getTarget());
        log.info("代理对象自己:" + joinPoint.getThis());
    }

    // 在每个日志横切面之后执行
    @After(value = "log()")
    public void doAfter(JoinPoint joinPoint){
        log.info("—————————————————doAfter—————————————————");
    }


//    // returning = "result"实现通过参数result捕获以上拦截方法的返回内容，pointcut = "log()"是取得切面
//    @AfterReturning(returning = "result",pointcut = "log()")
//    public void doAfterReturn(Object result){
//        log.info("—————————————————doAfterReturn—————————————————");
//        log.info("Result : {}", result);
//    }

    // pointcut/value:这两个属性的作用是一样的，它们都属于指定切入点对应的切入表达式。当指定了pointcut属性值后，value属性值将会被覆盖。
    // returning:该属性指定一个形参名，用于表示方法中可定义与此同名的形参，该形参可用于访问目标方法的返回值。
    // 所有被注解为后置通知的方法，除了可以加入JoinPoint参数外（不能使用ProceedingJoinPoint，会报错），还可以包含一个用于接收返回值的变量
    @AfterReturning(value = "log()",returning = "methodResult")
    public Object afterReturning(JoinPoint joinPoint, Object methodResult) {
        //获取方法返回值
        Object[] args = joinPoint.getArgs();
        log.info("—————————————————afterReturning—————————————————");
        log.info("Result : {}", methodResult);
        log.info("args : {}", args);
        return methodResult;
    }

    @Around("log()")
    public Object aroundMethod(ProceedingJoinPoint pjd){
        log.info("—————————————————aroundMethod—————————————————");
        Object result;
        try {
            Object[] args = pjd.getArgs();
            for (int i = 0; i < args.length; i++) {
                log.info("第" + (i+1) + "个参数为:" + args[i]);
            }
            //前置通知
            log.info("目标方法执行前...");
            //执行目标方法
            //result = pjd.proeed();
            //用新的参数值执行目标方法
            result = pjd.proceed(new Object[]{"newSpring"});
            //返回通知
            log.info("目标方法返回结果后...");
        } catch (Throwable e) {
            //异常通知
            log.error("执行目标方法异常后...");
            throw new RuntimeException(e);
        }
        //后置通知
        log.info("目标方法执行后...");
        return result;
    }

    @AfterThrowing("log()")
    public void doAfterThrowing(){
        log.info("—————————————————doAfterThrowing—————————————————");
    }

}
